ποΈGitΠ―ΡΠ°ποΈ
Node / meshtastic / Meshtastic-Android / files / core / takserver / src / commonTest / kotlin / org / meshtastic / core / takserver / TAKMeshIntegrationTest.kt
Displaying Raw β’ Download
core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKMeshIntegrationTest.kt 65a1f5ce4d0e4b8eafcd0bbfccafd41dc6ee888a (65a1f5ce) Text, 18.83 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.takserver
Tff7b72import T7ee787kotlinx.coroutines.flow.Flow
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.StateFlow
Tff7b72import T7ee787kotlinx.coroutines.test.UnconfinedTestDispatcher
Tff7b72import T7ee787kotlinx.coroutines.test.runTest
Tff7b72import T7ee787okio.ByteString.Companion.toByteString
Tff7b72import T7ee787org.meshtastic.core.di.CoroutineDispatchers
Tff7b72import T7ee787org.meshtastic.core.model.MyNodeInfo
Tff7b72import T7ee787org.meshtastic.core.model.Node
Tff7b72import T7ee787org.meshtastic.core.model.NodeSortOption
Tff7b72import T7ee787org.meshtastic.core.repository.MeshConfigHandler
Tff7b72import T7ee787org.meshtastic.core.repository.NodeRepository
Tff7b72import T7ee787org.meshtastic.core.repository.RadioSessionContext
Tff7b72import T7ee787org.meshtastic.core.testing.FakeCommandSender
Tff7b72import T7ee787org.meshtastic.core.testing.FakeServiceRepository
Tff7b72import T7ee787org.meshtastic.core.testing.FakeTakPrefs
Tff7b72import T7ee787org.meshtastic.proto.Channel
Tff7b72import T7ee787org.meshtastic.proto.Config
Tff7b72import T7ee787org.meshtastic.proto.Data
Tff7b72import T7ee787org.meshtastic.proto.DeviceMetadata
Tff7b72import T7ee787org.meshtastic.proto.DeviceUIConfig
Tff7b72import T7ee787org.meshtastic.proto.LoRaRegionPresetMap
Tff7b72import T7ee787org.meshtastic.proto.LocalConfig
Tff7b72import T7ee787org.meshtastic.proto.LocalModuleConfig
Tff7b72import T7ee787org.meshtastic.proto.LocalStats
Tff7b72import T7ee787org.meshtastic.proto.MeshPacket
Tff7b72import T7ee787org.meshtastic.proto.ModuleConfig
Tff7b72import T7ee787org.meshtastic.proto.PortNum
Tff7b72import T7ee787org.meshtastic.proto.TAKPacket
Tff7b72import T7ee787org.meshtastic.proto.User
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertNotNull
Tff7b72import T7ee787kotlin.test.assertTrue
Tff7b72import T7ee787kotlin.time.Clock
Tff7b72import T7ee787kotlin.time.Duration.Companion.minutes
T8b949e/**
* Tests for [TAKMeshIntegration] lifecycle, routing, and protocol gating.
*
* These tests use fakes for all 5 dependencies and run in commonTest. The v2 outbound SDK-dependent happy path is
* tested separately in jvmTest.
*/
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooManyFunctionsTa5d6ff"Tb4b4b4)
Tff7b72class T56d364TAKMeshIntegrationTest Tb4b4b4{
T8b949e// ββ Fakes ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
T8b949e// FakeTAKServerManager lives in its own file in this source set, shared with MeshToCotBroadcasterTest.
Tff7b72private Tff7b72class T56d364FakeMeshConfigHandler Tb4b4b4: Te6edf3MeshConfigHandler Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3localConfigTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3LocalConfigTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3LocalConfigTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3moduleConfigTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3LocalModuleConfigTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3LocalModuleConfigTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffhandleDeviceConfigTb4b4b4(Te6edf3configTb4b4b4: Te6edf3ConfigTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Tff7b72true
Tff7b72override Tff7b72fun Td2a8ffhandleModuleConfigTb4b4b4(Te6edf3configTb4b4b4: Te6edf3ModuleConfigTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Tff7b72true
Tff7b72override Tff7b72fun Td2a8ffhandleChannelTb4b4b4(Te6edf3channelTb4b4b4: Te6edf3ChannelTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Tff7b72true
Tff7b72override Tff7b72fun Td2a8ffhandleDeviceUIConfigTb4b4b4(Te6edf3configTb4b4b4: Te6edf3DeviceUIConfigTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Tff7b72true
Tff7b72override Tff7b72fun Td2a8ffhandleRegionPresetsTb4b4b4(Te6edf3mapTb4b4b4: Te6edf3LoRaRegionPresetMapTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Tff7b72true
Tb4b4b4}
Tff7b72private Tff7b72class T56d364FakeNodeRepositoryTb4b4b4(Te6edf3firmwareVersionTb4b4b4: Tffa657String? Tff7b72= Ta5d6ff"Ta5d6ff2.8.0.0Ta5d6ff"Tb4b4b4) Tb4b4b4: Te6edf3NodeRepository Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3_myNodeInfo Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3myNodeInfoTb4b4b4(Te6edf3firmwareVersionTb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3myNodeInfoTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3MyNodeInfo?Tff7b72> Tff7b72= Te6edf3_myNodeInfo
Tff7b72fun Td2a8ffsetFirmwareVersionTb4b4b4(Te6edf3versionTb4b4b4: Tffa657String?Tb4b4b4) Tb4b4b4{
Te6edf3_myNodeInfoTb4b4b4.Te6edf3value Tff7b72= Te6edf3myNodeInfoTb4b4b4(Te6edf3versionTb4b4b4)
Tb4b4b4}
Tff7b72private Tff7b72fun Td2a8ffmyNodeInfoTb4b4b4(Te6edf3firmwareVersionTb4b4b4: Tffa657String?Tb4b4b4) Tff7b72= Te6edf3MyNodeInfoTb4b4b4(
Te6edf3myNodeNum Tff7b72= T79c0ff1Tb4b4b4,
Te6edf3hasGPS Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3model Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3firmwareVersion Tff7b72= Te6edf3firmwareVersionTb4b4b4,
Te6edf3couldUpdate Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3shouldUpdate Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3currentPacketId Tff7b72= T79c0ff0LTb4b4b4,
Te6edf3messageTimeoutMsec Tff7b72= T79c0ff0Tb4b4b4,
Te6edf3minAppVersion Tff7b72= T79c0ff0Tb4b4b4,
Te6edf3maxChannels Tff7b72= T79c0ff8Tb4b4b4,
Te6edf3hasWifi Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3channelUtilization Tff7b72= T79c0ff0fTb4b4b4,
Te6edf3airUtilTx Tff7b72= T79c0ff0fTb4b4b4,
Te6edf3deviceId Tff7b72= Tff7b72nullTb4b4b4,
Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3ourNodeInfoTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3Node?Tff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72nullTb4b4b4)
Tff7b72override Tff7b72val Te6edf3myIdTb4b4b4: Te6edf3StateFlowTff7b72<Tffa657String?Tff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72nullTb4b4b4)
Tff7b72override Tff7b72val Te6edf3localStatsTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3LocalStatsTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3LocalStatsTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3nodeDBbyNumTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3MapTff7b72<Tffa657IntTb4b4b4, Te6edf3NodeTff7b72>Tff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3emptyMapTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetNodeDbSnapshotTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3MapTff7b72<Tffa657IntTb4b4b4, Te6edf3NodeTff7b72> Tff7b72= Te6edf3nodeDBbyNumTb4b4b4.Te6edf3value
Tff7b72override Tff7b72val Te6edf3onlineNodeCountTb4b4b4: Te6edf3FlowTff7b72<Tffa657IntTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(T79c0ff0Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3totalNodeCountTb4b4b4: Te6edf3FlowTff7b72<Tffa657IntTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(T79c0ff0Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffupdateLocalStatsTb4b4b4(Te6edf3statsTb4b4b4: Te6edf3LocalStatsTb4b4b4) Tb4b4b4{Tb4b4b4}
Tff7b72override Tff7b72fun Td2a8ffeffectiveLogNodeIdTb4b4b4(Te6edf3nodeNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3FlowTff7b72<Tffa657IntTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(T79c0ff0Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffgetNodeTb4b4b4(Te6edf3userIdTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3Node Tff7b72= Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff0Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffgetUserTb4b4b4(Te6edf3nodeNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3User Tff7b72= Te6edf3UserTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffgetUserTb4b4b4(Te6edf3userIdTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3User Tff7b72= Te6edf3UserTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffgetNodesTb4b4b4(
Te6edf3sortTb4b4b4: Te6edf3NodeSortOptionTb4b4b4,
Te6edf3filterTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3includeUnknownTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3onlyOnlineTb4b4b4: Tffa657BooleanTb4b4b4,
Te6edf3onlyDirectTb4b4b4: Tffa657BooleanTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3FlowTff7b72<Te6edf3ListTff7b72<Te6edf3NodeTff7b72>Tff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3emptyListTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetNodesOlderThanTb4b4b4(Te6edf3lastHeardTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3NodeTff7b72> Tff7b72= Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffgetUnknownNodesTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3NodeTff7b72> Tff7b72= Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffclearNodeDBTb4b4b4(Te6edf3preserveFavoritesTb4b4b4: Tffa657BooleanTb4b4b4) Tb4b4b4{Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffclearMyNodeInfoTb4b4b4(Tb4b4b4) Tb4b4b4{Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffdeleteNodeTb4b4b4(Te6edf3numTb4b4b4: Tffa657IntTb4b4b4) Tb4b4b4{Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffdeleteNodesTb4b4b4(Te6edf3nodeNumsTb4b4b4: Te6edf3ListTff7b72<Tffa657IntTff7b72>Tb4b4b4) Tb4b4b4{Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffsetNodeNotesTb4b4b4(Te6edf3numTb4b4b4: Tffa657IntTb4b4b4, Te6edf3notesTb4b4b4: Tffa657StringTb4b4b4) Tb4b4b4{Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffupsertTb4b4b4(Te6edf3nodeTb4b4b4: Te6edf3NodeTb4b4b4) Tb4b4b4{Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffinstallConfigTb4b4b4(Te6edf3miTb4b4b4: Te6edf3MyNodeInfoTb4b4b4, Te6edf3nodesTb4b4b4: Te6edf3ListTff7b72<Te6edf3NodeTff7b72>Tb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Tffa657IntTff7b72> Tff7b72= Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffinsertMetadataTb4b4b4(Te6edf3nodeNumTb4b4b4: Tffa657IntTb4b4b4, Te6edf3metadataTb4b4b4: Te6edf3DeviceMetadataTb4b4b4) Tb4b4b4{Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffupdatePowerChannelLabelTb4b4b4(Te6edf3numTb4b4b4: Tffa657IntTb4b4b4, Te6edf3channelIndexTb4b4b4: Tffa657IntTb4b4b4, Te6edf3labelTb4b4b4: Tffa657StringTb4b4b4) Tb4b4b4{Tb4b4b4}
Tb4b4b4}
T8b949e// ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tff7b72private Tff7b72data Tff7b72class T56d364TestHarnessTb4b4b4(
Tff7b72val Te6edf3serverManagerTb4b4b4: Te6edf3FakeTAKServerManager Tff7b72= Te6edf3FakeTAKServerManagerTb4b4b4(Tb4b4b4)Tb4b4b4,
Tff7b72val Te6edf3commandSenderTb4b4b4: Te6edf3FakeCommandSender Tff7b72= Te6edf3FakeCommandSenderTb4b4b4(Tb4b4b4)Tb4b4b4,
Tff7b72val Te6edf3serviceRepositoryTb4b4b4: Te6edf3FakeServiceRepository Tff7b72= Te6edf3FakeServiceRepositoryTb4b4b4(Tb4b4b4)Tb4b4b4,
Tff7b72val Te6edf3meshConfigHandlerTb4b4b4: Te6edf3FakeMeshConfigHandler Tff7b72= Te6edf3FakeMeshConfigHandlerTb4b4b4(Tb4b4b4)Tb4b4b4,
Tff7b72val Te6edf3nodeRepositoryTb4b4b4: Te6edf3FakeNodeRepository Tff7b72= Te6edf3FakeNodeRepositoryTb4b4b4(Tb4b4b4)Tb4b4b4,
Tff7b72val Te6edf3takPrefsTb4b4b4: Te6edf3FakeTakPrefs Tff7b72= Te6edf3FakeTakPrefsTb4b4b4(Tb4b4b4)Tb4b4b4,
Tff7b72val Te6edf3dispatchersTb4b4b4: Te6edf3CoroutineDispatchers Tff7b72=
Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3let Tb4b4b4{ Te6edf3CoroutineDispatchersTb4b4b4(Te6edf3io Tff7b72= Tffa657itTb4b4b4, Te6edf3main Tff7b72= Tffa657itTb4b4b4, Te6edf3default Tff7b72= Tffa657itTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4) Tb4b4b4{
T8b949e// Mesh-to-CoT is opt-in and FakeTakPrefs defaults it off, so it stays inert here.
Tff7b72val Te6edf3broadcaster Tff7b72= Te6edf3MeshToCotBroadcasterTb4b4b4(Te6edf3serverManagerTb4b4b4, Te6edf3nodeRepositoryTb4b4b4, Te6edf3takPrefsTb4b4b4, Te6edf3dispatchersTb4b4b4)
Tff7b72val Te6edf3integration Tff7b72=
Te6edf3TAKMeshIntegrationTb4b4b4(
Te6edf3takServerManager Tff7b72= Te6edf3serverManagerTb4b4b4,
Te6edf3commandSender Tff7b72= Te6edf3commandSenderTb4b4b4,
Te6edf3serviceRepository Tff7b72= Te6edf3serviceRepositoryTb4b4b4,
Te6edf3meshConfigHandler Tff7b72= Te6edf3meshConfigHandlerTb4b4b4,
Te6edf3nodeRepository Tff7b72= Te6edf3nodeRepositoryTb4b4b4,
Te6edf3meshToCotBroadcaster Tff7b72= Te6edf3broadcasterTb4b4b4,
Tb4b4b4)
Tb4b4b4}
T8b949e// ββ Lifecycle tests ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tf0883e@Test
Tff7b72fun Td2a8ff`start launches TAKServerManager`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3startCountTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`stop cancels jobs and stops TAKServerManager`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3stoppedTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`double start is idempotent`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4) T8b949e// second start
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3startCountTb4b4b4, Ta5d6ff"Ta5d6ffTAKServerManager should only be started onceTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`stop then inbound TAK message does not forward to mesh`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3emitInboundTb4b4b4(Te6edf3createPliTb4b4b4(Ta5d6ff"Ta5d6ffafter-stopTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
T8b949e// ββ TAK-Talk strip preservation (regression) ββββββββββββββββββββββββββββ
Tf0883e@Test
Tff7b72fun Td2a8ff`stripNonEssentialElements preserves TAK-Talk voice and marti`Tb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// Regression guard: <voice/> (push-to-talk marker) and <marti><dest .../>
T8b949e// </marti> (directed routing) were once added to STRIP_PATTERNS, which
T8b949e// silently broke TAK-Talk end-to-end β ATAK received the m-t-t CoT but its
T8b949e// plugin could neither play (no <voice/>) nor route (no <marti/>) it. Both
T8b949e// MUST survive the send-side strip.
Tff7b72val Te6edf3mtt Tff7b72=
Ta5d6ff"""
Ta5d6ff <event version=Ta5d6ff"Ta5d6ff2.0Ta5d6ff"Ta5d6ff uid=Ta5d6ff"Ta5d6ffTAKTALK-MESSAGE-testTa5d6ff"Ta5d6ff type=Ta5d6ff"Ta5d6ffm-t-tTa5d6ff"Ta5d6ff how=Ta5d6ff"Ta5d6ffnullTa5d6ff"Ta5d6ff time=Ta5d6ff"Ta5d6fftTa5d6ff"Ta5d6ff start=Ta5d6ff"Ta5d6fftTa5d6ff"Ta5d6ff stale=Ta5d6ff"Ta5d6fftTa5d6ff"Ta5d6ff>
<point lat=Ta5d6ff"Ta5d6ff0.0Ta5d6ff"Ta5d6ff lon=Ta5d6ff"Ta5d6ff0.0Ta5d6ff"Ta5d6ff hae=Ta5d6ff"Ta5d6ff9999999.0Ta5d6ff"Ta5d6ff ce=Ta5d6ff"Ta5d6ff9999999.0Ta5d6ff"Ta5d6ff le=Ta5d6ff"Ta5d6ff9999999.0Ta5d6ff"Ta5d6ff/>
<detail>
<callsign>ASPEN</callsign><lang>English</lang><text>Testing 123</text>
<chatroom-id>1</chatroom-id><takv version=Ta5d6ff"Ta5d6ffxTa5d6ff"Ta5d6ff/><voice/>
<marti><dest callsign=Ta5d6ff"Ta5d6ffETHELTa5d6ff"Ta5d6ff/></marti>
</detail>
</event>
Ta5d6ff"""
Tb4b4b4.Te6edf3trimIndentTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3stripped Tff7b72= Te6edf3TAKMeshIntegrationTb4b4b4.Te6edf3stripNonEssentialElementsTb4b4b4(Te6edf3mttTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3strippedTb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ff<voice/>Ta5d6ff"Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffTAK-Talk <voice/> PTT marker must survive stripTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(
Te6edf3strippedTb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ff<marti>Ta5d6ff"Tb4b4b4) Tff7b72&Tff7b72& Te6edf3strippedTb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ffdest callsign=Ta5d6ff\"Ta5d6ffETHELTa5d6ff\"Ta5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffTAK-Talk <marti> directed-routing must survive stripTa5d6ff"Tb4b4b4,
Tb4b4b4)
T8b949e// Sanity: genuinely non-essential elements are still stripped.
Te6edf3assertTrueTb4b4b4(Tff7b72!Te6edf3strippedTb4b4b4.Te6edf3containsTb4b4b4(Ta5d6ff"Ta5d6ff<takvTa5d6ff"Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffnon-essential <takv> should still be strippedTa5d6ff"Tb4b4b4)
Tb4b4b4}
T8b949e// ββ Inbound mesh β TAK client (V1) ββββββββββββββββββββββββββββββββββββββ
Tf0883e@Test
Tff7b72fun Td2a8ff`inbound V1 PLI packet is broadcast to TAK clients`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3serviceRepositoryTb4b4b4.Te6edf3emitMeshPacketTb4b4b4(Te6edf3createV1PliMeshPacketTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffExpected broadcasts for V1 PLITa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3typeTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"Ta5d6ffa-f-Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`inbound packet on unrelated port is ignored`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Tff7b72val Te6edf3textPacket Tff7b72=
Te6edf3MeshPacketTb4b4b4(
Te6edf3decoded Tff7b72=
Te6edf3DataTb4b4b4(Te6edf3portnum Tff7b72= Te6edf3PortNumTb4b4b4.Te6edf3TEXT_MESSAGE_APPTb4b4b4, Te6edf3payload Tff7b72= Ta5d6ff"Ta5d6ffhelloTa5d6ff"Tb4b4b4.Te6edf3encodeToByteArrayTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3toByteStringTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3serviceRepositoryTb4b4b4.Te6edf3emitMeshPacketTb4b4b4(Te6edf3textPacketTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3rawBroadcastsTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
T8b949e// ββ Firmware gating ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tf0883e@Test
Tff7b72fun Td2a8ff`unknown firmware uses V1 protocol`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Te6edf3nodeRepository Tff7b72= Te6edf3FakeNodeRepositoryTb4b4b4(Te6edf3firmwareVersion Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3emitInboundTb4b4b4(Te6edf3createPliTb4b4b4(Ta5d6ff"Ta5d6fftest-v1-until-version-knownTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3sizeTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3PortNumTb4b4b4.Te6edf3ATAK_PLUGINTb4b4b4.Te6edf3valueTb4b4b4, Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3singleTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3dataTypeTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`V2-capable firmware sends V2 protocol`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Te6edf3nodeRepository Tff7b72= Te6edf3FakeNodeRepositoryTb4b4b4(Te6edf3firmwareVersion Tff7b72= Ta5d6ff"Ta5d6ff2.8.0.0Ta5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3emitInboundTb4b4b4(Te6edf3createPliTb4b4b4(Ta5d6ff"Ta5d6fftest-v2-knownTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3sizeTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3PortNumTb4b4b4.Te6edf3ATAK_PLUGIN_V2Tb4b4b4.Te6edf3valueTb4b4b4, Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3singleTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3dataTypeTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`legacy firmware sends on V1 port 72`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Te6edf3nodeRepository Tff7b72= Te6edf3FakeNodeRepositoryTb4b4b4(Te6edf3firmwareVersion Tff7b72= Ta5d6ff"Ta5d6ff2.7.0.0Ta5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3emitInboundTb4b4b4(Te6edf3createPliTb4b4b4(Ta5d6ff"Ta5d6fftest-v1Ta5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3sizeTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3PortNumTb4b4b4.Te6edf3ATAK_PLUGINTb4b4b4.Te6edf3valueTb4b4b4, Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3singleTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3dataTypeTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`legacy firmware drops non-PLI non-GeoChat types`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Te6edf3nodeRepository Tff7b72= Te6edf3FakeNodeRepositoryTb4b4b4(Te6edf3firmwareVersion Tff7b72= Ta5d6ff"Ta5d6ff2.7.0.0Ta5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Tff7b72val Te6edf3marker Tff7b72= Te6edf3CoTMessageTb4b4b4(Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffmarker-1Ta5d6ff"Tb4b4b4, Te6edf3type Tff7b72= Ta5d6ff"Ta5d6ffa-h-GTa5d6ff"Tb4b4b4, Te6edf3stale Tff7b72= Te6edf3ClockTb4b4b4.Te6edf3SystemTb4b4b4.Te6edf3nowTb4b4b4(Tb4b4b4) Tff7b72+ T79c0ff5.Te6edf3minutesTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3emitInboundTb4b4b4(Te6edf3markerTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
T8b949e// ββ Dropped-outcome discrimination (regression: #6583 self-test blind spot) ββββββββ
Tf0883e@Test
Tff7b72fun Td2a8ff`v1 drop of an unsupported CoT type is schema-limited`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// a-h-G is a shape/marker type the legacy v1 TAKPacket schema has no field for at all β
T8b949e// this is the "permanent limitation" case, not a size problem.
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Te6edf3nodeRepository Tff7b72= Te6edf3FakeNodeRepositoryTb4b4b4(Te6edf3firmwareVersion Tff7b72= Ta5d6ff"Ta5d6ff2.7.0.0Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3marker Tff7b72= Te6edf3CoTMessageTb4b4b4(Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffmarker-1Ta5d6ff"Tb4b4b4, Te6edf3type Tff7b72= Ta5d6ff"Ta5d6ffa-h-GTa5d6ff"Tb4b4b4, Te6edf3stale Tff7b72= Te6edf3ClockTb4b4b4.Te6edf3SystemTb4b4b4.Te6edf3nowTb4b4b4(Tb4b4b4) Tff7b72+ T79c0ff5.Te6edf3minutesTb4b4b4)
Tff7b72val Te6edf3outcome Tff7b72= Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3sendCoTToMeshForTestTb4b4b4(Te6edf3markerTb4b4b4, Te6edf3forceV2 Tff7b72= Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3dropped Tff7b72= Te6edf3assertNotNullTb4b4b4(Te6edf3outcome Tff7b72as? Te6edf3TakSendOutcomeTb4b4b4.Te6edf3DroppedTb4b4b4, Ta5d6ff"Ta5d6ffexpected a Dropped outcome, got Tffd700$Te6edf3outcomeTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3droppedTb4b4b4.Te6edf3schemaLimitedTb4b4b4, Ta5d6ff"Ta5d6ffan unsupported CoT type must be reported as schema-limitedTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`v1 drop of an oversize but schema-representable PLI is NOT schema-limited`Tb4b4b4(Tb4b4b4) Tff7b72=
Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// a-f-G (PLI) IS representable in the v1 schema β this must be dropped for size, not
T8b949e// mislabeled as an expected schema gap. This is the exact blind spot the self-test's
T8b949e// expectedDrop flag has to avoid: an MTU problem hiding behind "expected" v1 behavior.
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Te6edf3nodeRepository Tff7b72= Te6edf3FakeNodeRepositoryTb4b4b4(Te6edf3firmwareVersion Tff7b72= Ta5d6ff"Ta5d6ff2.7.0.0Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3oversizePli Tff7b72=
Te6edf3CoTMessageTb4b4b4(
Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffpli-1Ta5d6ff"Tb4b4b4,
Te6edf3type Tff7b72= Ta5d6ff"Ta5d6ffa-f-G-U-CTa5d6ff"Tb4b4b4,
Te6edf3stale Tff7b72= Te6edf3ClockTb4b4b4.Te6edf3SystemTb4b4b4.Te6edf3nowTb4b4b4(Tb4b4b4) Tff7b72+ T79c0ff5.Te6edf3minutesTb4b4b4,
Te6edf3contact Tff7b72= Te6edf3CoTContactTb4b4b4(Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffXTa5d6ff"Tb4b4b4.Te6edf3repeatTb4b4b4(T79c0ff5T79c0ff0T79c0ff0Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3outcome Tff7b72= Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3sendCoTToMeshForTestTb4b4b4(Te6edf3oversizePliTb4b4b4, Te6edf3forceV2 Tff7b72= Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3dropped Tff7b72= Te6edf3assertNotNullTb4b4b4(Te6edf3outcome Tff7b72as? Te6edf3TakSendOutcomeTb4b4b4.Te6edf3DroppedTb4b4b4, Ta5d6ff"Ta5d6ffexpected a Dropped outcome, got Tffd700$Te6edf3outcomeTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Tff7b72!Te6edf3droppedTb4b4b4.Te6edf3schemaLimitedTb4b4b4, Ta5d6ff"Ta5d6ffan oversize drop of a representable type must not be schema-limitedTa5d6ff"Tb4b4b4)
Tb4b4b4}
T8b949e// ββ GeoChat callsign enrichment ββββββββββββββββββββββββββββββββββββββββββ
Tf0883e@Test
Tff7b72fun Td2a8ff`GeoChat without callsign is enriched from client info`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3TestHarnessTb4b4b4(Te6edf3nodeRepository Tff7b72= Te6edf3FakeNodeRepositoryTb4b4b4(Te6edf3firmwareVersion Tff7b72= Ta5d6ff"Ta5d6ff2.7.0.0Ta5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3integrationTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Tff7b72val Te6edf3chatMsg Tff7b72=
Te6edf3CoTMessageTb4b4b4(
Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffGeoChat.test.All Chat Rooms.1234Ta5d6ff"Tb4b4b4,
Te6edf3type Tff7b72= Ta5d6ff"Ta5d6ffb-t-fTa5d6ff"Tb4b4b4,
Te6edf3how Tff7b72= Ta5d6ff"Ta5d6ffh-g-i-g-oTa5d6ff"Tb4b4b4,
Te6edf3stale Tff7b72= Te6edf3ClockTb4b4b4.Te6edf3SystemTb4b4b4.Te6edf3nowTb4b4b4(Tb4b4b4) Tff7b72+ T79c0ff5.Te6edf3minutesTb4b4b4,
Te6edf3contact Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3chat Tff7b72= Te6edf3CoTChatTb4b4b4(Te6edf3message Tff7b72= Ta5d6ff"Ta5d6ffhelloTa5d6ff"Tb4b4b4, Te6edf3senderCallsign Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3clientInfo Tff7b72= Te6edf3TAKClientInfoTb4b4b4(Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ffclient-1Ta5d6ff"Tb4b4b4, Te6edf3endpoint Tff7b72= Ta5d6ff"Ta5d6ff127.0.0.1:8089Ta5d6ff"Tb4b4b4, Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffALPHA-1Ta5d6ff"Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3emitInboundTb4b4b4(Te6edf3chatMsgTb4b4b4, Te6edf3clientInfoTb4b4b4)
T8b949e// GeoChat on legacy V1 should produce a sent packet with the enriched callsign
Tff7b72if Tb4b4b4(Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3sent Tff7b72= Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3PortNumTb4b4b4.Te6edf3ATAK_PLUGINTb4b4b4.Te6edf3valueTb4b4b4, Te6edf3sentTb4b4b4.Te6edf3dataTypeTb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e// ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tff7b72private Tff7b72fun Td2a8ffcreatePliTb4b4b4(Te6edf3uidTb4b4b4: Tffa657StringTb4b4b4) Tff7b72=
Te6edf3CoTMessageTb4b4b4.Te6edf3pliTb4b4b4(Te6edf3uid Tff7b72= Te6edf3uidTb4b4b4, Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffTESTTa5d6ff"Tb4b4b4, Te6edf3latitude Tff7b72= T79c0ff3T79c0ff3.0Tb4b4b4, Te6edf3longitude Tff7b72= Tff7b72-T79c0ff8T79c0ff4.0Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffcreateV1PliMeshPacketTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3MeshPacket Tb4b4b4{
Tff7b72val Te6edf3takPacket Tff7b72=
Te6edf3TAKPacketTb4b4b4(
Te6edf3contact Tff7b72= Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3ContactTb4b4b4(Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffBRAVOTa5d6ff"Tb4b4b4, Te6edf3device_callsign Tff7b72= Ta5d6ff"Ta5d6ffbravo-uidTa5d6ff"Tb4b4b4)Tb4b4b4,
Te6edf3pli Tff7b72=
Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3PLITb4b4b4(
Te6edf3latitude_i Tff7b72= T79c0ff3T79c0ff3T79c0ff0T79c0ff0T79c0ff0T79c0ff0T79c0ff0T79c0ff0T79c0ff0Tb4b4b4,
Te6edf3longitude_i Tff7b72= Tff7b72-T79c0ff8T79c0ff4T79c0ff0T79c0ff0T79c0ff0T79c0ff0T79c0ff0T79c0ff0T79c0ff0Tb4b4b4,
Te6edf3altitude Tff7b72= T79c0ff1T79c0ff0T79c0ff0Tb4b4b4,
Te6edf3speed Tff7b72= T79c0ff0Tb4b4b4,
Te6edf3course Tff7b72= T79c0ff0Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Te6edf3group Tff7b72=
Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3GroupTb4b4b4(
Te6edf3team Tff7b72= Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3TeamTb4b4b4.Te6edf3CyanTb4b4b4,
Te6edf3role Tff7b72= Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3MemberRoleTb4b4b4.Te6edf3TeamMemberTb4b4b4,
Tb4b4b4)Tb4b4b4,
Te6edf3status Tff7b72= Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3StatusTb4b4b4(Te6edf3battery Tff7b72= T79c0ff8T79c0ff5Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3MeshPacketTb4b4b4(
Te6edf3decoded Tff7b72= Te6edf3DataTb4b4b4(Te6edf3portnum Tff7b72= Te6edf3PortNumTb4b4b4.Te6edf3ATAK_PLUGINTb4b4b4, Te6edf3payload Tff7b72= Te6edf3TAKPacketTb4b4b4.Te6edf3ADAPTERTb4b4b4.Te6edf3encodeTb4b4b4(Te6edf3takPacketTb4b4b4)Tb4b4b4.Te6edf3toByteStringTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.11s